#include <streambuf>
#include <iostream>
#include <string>
#include"FunctionPtr.h"
#include"Functions.h"

//using namespace std;


FunctionPtr::FunctionPtr() : ptr_(0), copy_counter_(0) {}

FunctionPtr::FunctionPtr(std::string const & function) : ptr_( createObject(function) ), copy_counter_(new int(1)) {}

FunctionPtr::~FunctionPtr() {
    if((*copy_counter_) == 1) {
        delete ptr_;
        delete copy_counter_;
    } else {
        --(*copy_counter_);
    }
}

FunctionPtr::FunctionPtr(FunctionPtr const & copy): ptr_(copy.ptr_) {
    copy_counter_ = copy.copy_counter_;
    ++(*copy_counter_);
}

Function & FunctionPtr::operator = (FunctionPtr const & right) {
    if(ptr_ != right.ptr_) {
        FunctionPtr(right).swap(*this);
        ++(*copy_counter_);
    }
    return *ptr_;
}

void FunctionPtr::operator = (std::string const & right) {
     FunctionPtr(right).swap(*this);
}

Function * FunctionPtr::operator -> () const {
    return ptr_;
}

Function & FunctionPtr::operator * () const {
    return *ptr_;
}

bool FunctionPtr::operator !=(FunctionPtr const & right) const {
    return (ptr_ != right.ptr_);
}

bool FunctionPtr::operator ==(FunctionPtr const & right) const {
    return (ptr_ == right.ptr_);
}

void FunctionPtr::swap(FunctionPtr & right) {
    std::swap(ptr_, right.ptr_);
    std::swap(copy_counter_, right.copy_counter_);
}

Function * FunctionPtr::createObject(std::string const & func) const {
    char delimiters[] = {'+', '-', '*', '/'};
    Function * left = 0;
    Function * right = 0;
    size_t i = 0;
    while(i < 4) {
        size_t index = func.rfind( delimiters[i] );
        if(index != std::string::npos) {
            left = createObject(func.substr(0, index));
            right = createObject(func.substr(index + 1, -1));
            break;
        }
        ++i;
    }
    if(i == 0) return new Plus(left, right);
    if(i == 1) return new Minus(left, right);
    if(i == 2) return new Mult(left, right);
    if(i == 3) return new Devide(left, right);
    if(func == "x") return new Var();
    return new Const(func);
}